<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>American Migration Graph</title>
  </head>
  <style>
    body {
      background: rgb(0, 0, 0);
    }
    .g6-tooltip {
      border: 1px solid #e2e2e2;
      border-radius: 4px;
      font-size: 12px;
      color: #545454;
      background-color: rgba(255, 255, 255, 0.9);
      padding: 10px 8px;
      box-shadow: rgb(174, 174, 174) 0px 0px 10px;
    }
  </style>
  <body>
    <div id="mountNode"></div>
    <script src="../build/g6.js"></script>
    <script src="./assets/d3-4.13.0.min.js"></script>
    <script src="../build/bundling.js"></script>
    <script>
      const lightBlue = 'rgb(119, 243, 252)';
      const llightBlue16 = '#C8FDFC';
      const lightOrange = 'rgb(230, 100, 64)';
      const llightOrange16 = '#FFAA86';
      d3.json('./assets/data/american-migration.json', function(data) {
        const nodes = data.nodes;
        const edges = data.edges;
        nodes.forEach(n => {
          n.y = -n.y;
          n.degree = 0;
          n.inDegree = 0;
          n.outDegree = 0;
        });
        // compute the degree of each node
        const nodeIdMap = new Map();
        nodes.forEach(node => {
          nodeIdMap.set(node.id, node);
        });
        edges.forEach(e => {
          const source = nodeIdMap.get(e.source);
          const target = nodeIdMap.get(e.target);
          source.outDegree++;
          target.inDegree++;
          source.degree++;
          target.degree++;
          if (e.sytle === undefined) e.style = {};
          e.style.lineWidth = 0.7;
          e.style.strokeOpacity = 0.1;
          e.style.stroke = 'l(0) 0:' + llightOrange16 + ' 1:' + llightBlue16;
          if (nodeIdMap.get(e.source).x < nodeIdMap.get(e.target).x) {
            e.style.stroke = 'l(0) 0:' + llightBlue16 + ' 1:' + llightOrange16;
          }
        });
        let maxDegree = -9999,
          minDegree = 9999;
        nodes.forEach(n => {
          if (maxDegree < n.degree) maxDegree = n.degree;
          if (minDegree > n.degree) minDegree = n.degree;
        });
        const sizeRange = [1, 20];
        const sizeDataRange = [minDegree, maxDegree];
        scaleNodeProp(nodes, 'size', 'degree', sizeDataRange, sizeRange);

        // customize the node
        G6.registerNode(
          'pie-node',
          {
            drawShape: (cfg, group) => {
              const radius = cfg.size / 2;
              const inPercentage = cfg.inDegree / cfg.degree;
              const inAngle = inPercentage * Math.PI * 2;
              const outAngle = Math.PI * 2 - inAngle;
              const inArcEnd = [radius * Math.cos(inAngle), radius * Math.sin(inAngle)];
              let isInBigArc = 1,
                isOutBigArc = 0;
              if (inAngle > Math.PI) {
                isInBigArc = 0;
                isOutBigArc = 1;
              }
              const fanIn = group.addShape('path', {
                attrs: {
                  path: [
                    ['M', radius, 0],
                    ['A', radius, radius, 0, isInBigArc, 0, inArcEnd[0], inArcEnd[1]],
                    ['L', 0, 0],
                    ['B'],
                  ],
                  stroke: lightOrange,
                  lineWidth: 0,
                  fill: lightOrange,
                },
              });

              const fanOut = group.addShape('path', {
                attrs: {
                  path: [
                    ['M', inArcEnd[0], inArcEnd[1]],
                    ['A', radius, radius, 0, isOutBigArc, 0, radius, 0],
                    ['L', 0, 0],
                    ['B'],
                  ],
                  stroke: lightBlue,
                  lineWidth: 0,
                  fill: lightBlue,
                },
              });
              return fanIn;
            },
          },
          'single-shape',
        );

        const edgeBundling = new Bundling({
          bundleThreshold: 0.6,
          K: 100,
        });
        const graph = new G6.Graph({
          container: 'mountNode',
          width: 1000,
          height: 800,
          plugins: [edgeBundling],
          fitView: true,
          defaultNode: {
            shape: 'pie-node',
            size: 3,
            color: 'steelblue',
            style: {
              lineWidth: 0,
              fill: 'steelblue',
            },
          },
          modes: {
            default: [
              'drag-canvas',
              'zoom-canvas',
              {
                type: 'tooltip',
                formatText(model) {
                  const text = 'Longitude: ' + model.lon + '\n Latitude: ' + model.lat;
                  return text;
                },
                shouldUpdate: e => {
                  return true;
                },
              },
            ],
          },
        });

        edgeBundling.bundling(data);
        graph.data(data);
        graph.render();
      });
      function scaleNodeProp(nodes, propName, refPropName, dataRange, outRange) {
        const outLength = outRange[1] - outRange[0];
        const dataLength = dataRange[1] - dataRange[0];
        nodes.forEach(n => {
          n[propName] = ((n[refPropName] - dataRange[0]) * outLength) / dataLength + outRange[0];
        });
      }
    </script>
  </body>
</html>
